package com.rtsapp.server.benchmark.cases.asst;

import com.rtsapp.server.benchmark.exp.ExpUtils;
import com.rtsapp.server.utils.StringUtils;
import junit.framework.Assert;

import java.util.Map;

/**
 * Created by admin on 15-9-16.
 */
public class AssertUtils {

    private static final String[] assertTypes = new String[]{
            "assert.equals",
            "assert.notequals",
            "assert.null",
            "assert.true",
            "assert.false"
    };

    private static final IAssert[] assertObjs = new IAssert[]{
            new AssertEquals( true ),
            new AssertEquals( false ),
            new AssertNull(),
            new AssertTrueFalse( true ),
            new AssertTrueFalse( false )
    };

    /**
     * 根据断言的类型，创建一个AssertInfo并返回
     *
     * @param assertType
     * @return null如果断言类型不支持
     */
    public static AssertInfo createAssertInfo(String assertType) {

        if (StringUtils.isEmpty(assertType)) {
            return null;
        }

        for (int i = 0; i < assertTypes.length; i++) {
            if (assertTypes[i].equalsIgnoreCase(assertType)) {
                return new AssertInfo(i);
            }
        }

        return null;
    }


    public static void doAssert(AssertInfo assertInfo, Map context) {
        doAssert(assertInfo, context, null);
    }

    /**
     * 执行断言检测
     *
     * @param assertInfo 断言信息
     * @param context    上下文
     * @param expObj     不为空, 表示断言的表达式取值的对象, 为空, 从上下文计算断言表达式, 值的表达式不从expObj中取
     */
    public static void doAssert(AssertInfo assertInfo, Map context, Object expObj) {

        if (!assertInfo.hasAssert()) {
            return;
        }

        IAssert assertObj = getAssertObj(assertInfo.getAssertType());

        for ( AssertInfo.AssertExpPair pair : assertInfo.getExpValueList()) {
            String exp = pair.getActualExp( );
            String exceptValueExp = pair.getExpectExp( );
            assertObj.doAssert(context, expObj, exp, exceptValueExp);
        }
    }


    private static IAssert getAssertObj(int assertType) {
        return assertObjs[assertType];
    }


    private static interface IAssert {
        void doAssert(Map context, Object expObj, String exp, String ecceptValueExp);
    }


    /**
     * 用于 整型( byte, short, int, long ), String的断言
     */
    private static class AssertEquals implements IAssert {

        private final boolean assertEquals;

        public AssertEquals( boolean assertTrue  ){
            this.assertEquals = assertTrue;
        }


        @Override
        public void doAssert(Map context, Object expObj, String exp, String ecceptValueExp) {

            Object actualValue = null;
            if (expObj == null) {
                actualValue = ExpUtils.getValue(context, exp);
            } else {
                actualValue = ExpUtils.getValue(expObj, exp);
            }

            Object ecceptValue = ExpUtils.getValue(context, ecceptValueExp);

            try {
                if(assertEquals) {
                    Assert.assertEquals(ecceptValue, actualValue);
                }else{
                    if( ecceptValue == null ){
                        Assert.assertNotNull( actualValue );
                    }else{
                        Assert.assertFalse(ecceptValue.equals(actualValue));
                    }
                }
            } catch (junit.framework.AssertionFailedError ex) {

                if( actualValue instanceof  Integer ) {

                    Integer iValue = (Integer)actualValue;

                    throw new AssertFailedException("assert " + ( assertEquals ? "equals" : "notEquals" ) + " fail: 预期:[表达式=" + ecceptValueExp + ", 值=" + ecceptValue + "], 实际:[表达式=" + exp + ",值="+actualValue+"(0x" + (Integer.toHexString( iValue ) ) + ")]", ex);
                }else{
                    throw new AssertFailedException("assert " + ( assertEquals ? "equals" : "notEquals" ) + " fail: 预期:[表达式=" + ecceptValueExp + ", 值=" + ecceptValue + "], 实际:[表达式=" + exp + ",值="+actualValue+"]", ex);
                }

            }
        }

    }

    private static class AssertNull implements IAssert {

        @Override
        public void doAssert(Map context, Object expObj, String exp, String ecceptValueExp) {

            Object actualValue = null;
            if (expObj == null) {
                actualValue = ExpUtils.getValue(context, exp);
            } else {
                actualValue = ExpUtils.getValue(expObj, exp);
            }

            boolean isNull = ( "null".equalsIgnoreCase(ecceptValueExp) || "true".equalsIgnoreCase(ecceptValueExp) );
            try {

                if ( isNull ) {
                    Assert.assertNull(actualValue);
                } else {
                    Assert.assertNotNull(actualValue);
                }

            } catch (junit.framework.AssertionFailedError ex) {
                if( actualValue instanceof Integer ){
                    Integer iValue = (Integer)actualValue;

                    throw new AssertFailedException("assert fail: 预期:[表达式=" + ecceptValueExp + ", isNull=" + isNull + "], 实际:[表达式=" + exp + ",值=" + actualValue + "( 0x" + Integer.toHexString( iValue ) +")]", ex);
                }else {
                    throw new AssertFailedException("assert fail: 预期:[表达式=" + ecceptValueExp + ", isNull=" + isNull + "], 实际:[表达式=" + exp + ",值=" + actualValue + "]", ex);
                }
            }

        }
    }

    private static class AssertTrueFalse implements IAssert {

        private final boolean isTrue;

        AssertTrueFalse( boolean isTrue  ) {
            this.isTrue = isTrue;
        }
        @Override
        public void doAssert(Map context, Object expObj, String exp, String ecceptValueExp) {

            //对于AssertTrue， 忽略exp, 直接取值 ecceptValueExp
            Object actualValue = null;
            if (expObj == null) {
                actualValue = ExpUtils.getValue(context, ecceptValueExp);
            } else {
                actualValue = ExpUtils.getValue(expObj, ecceptValueExp);
            }

            try {

                Assert.assertTrue( actualValue instanceof  Boolean );

                Boolean boolValue = (Boolean)actualValue;
                if( isTrue ) {
                    Assert.assertTrue( boolValue );
                }else{
                    Assert.assertFalse(boolValue) ;
                }
            } catch (junit.framework.AssertionFailedError ex) {
                throw new AssertFailedException("assert fail: 预期:[表达式=" + ecceptValueExp + ", isTrue=" + isTrue + "], 实际:[值="+actualValue+"]", ex);
            }
        }
    }

}
